package main

import (
	"fmt"
	"time"
)

/*
1、channel可以声明为只读，或者只写性质(主要解决误操作)
2、使用select可以解决从管道取数据的阻塞问题
3、goroutine中使用recover，解决协程中出现panic，导致程序崩溃问题
说明：如果我们起了一个协程，但是这个协程出现了panic，如果我们没有捕获这个panic，
就会造成整个程序崩溃，这时我们可以在goroutine中使用recover来捕获panic，进行处理。
这样即使这个协程发生了问题，但是主线程仍然不受影响，可以继续执行
*/
//3
func sayHello() {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		fmt.Println("hello,world")
	}
}

//3
func test() {
	//这里可以使用defer + recover
	defer func() {
		//捕获test抛出的panic
		if err := recover(); err != nil {
			fmt.Println("test() 发生错误", err)
		}
	}()
	//定义了一个map
	var myMap map[int]string
	myMap[0] = "golang" //error
}
func main() {
	//1默认情况下，管道是双向的
	// var chan1 chan int //可读可写

	//声明为只写
	var chan2 chan<- int
	chan2 = make(chan int, 3)
	chan2 <- 20
	fmt.Println("chan2=", chan2)

	//声明为只读
	var chan3 <-chan int
	chan3 = make(chan int, 3)
label:
	for {
		select {
		case num2 := <-chan3:
			fmt.Printf("读取的数据=%v\n", num2)
		default:
			fmt.Printf("没取到数据\n")
			break label
		}
	}

	//2使用select可以解决从管道取数据的阻塞问题
	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan <- i
	}
	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan <- "hello" + fmt.Sprintf("%d", i)
	}
	//传统的方法在遍历管道时，如果不关闭会阻塞而导致deadlock
	//问题，在实际开发中，可能不好确定什么时候关闭该管道
	//可以使用select方式解决
label2:
	for {
		select {
		//注意：这里如果intChan一直没有关闭，不会一直阻塞而死锁
		//会自动到下一个case匹配
		case v := <-intChan:
			fmt.Printf("从intChan读取的数据%d\n", v)
		case v := <-stringChan:
			fmt.Printf("从intChan读取的数据%s\n", v)
		default:
			fmt.Printf("都取不到了，不玩了，程序员可以加入逻辑\n")
			// return
			break label2
		}
	}

	//3
	go sayHello()
	go test()
	for i := 0; i < 10; i++ {
		fmt.Println("main() ok=", i)
	}
}
